home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Compression / Opener / Source / utils / unsit / unsit.c < prev    next >
C/C++ Source or Header  |  1993-08-06  |  25KB  |  962 lines

  1. /*
  2.            unsit - Macintosh StuffIt file extractor
  3.  
  4.              Version 1.5f, for StuffIt 1.5
  5.  
  6.                 July 23, 1990
  7.  
  8. This program will unpack a Macintosh StuffIt file into separate files.
  9. The data fork of a StuffIt file contains both the data and resource
  10. forks of the packed files.  The program will unpack each Mac file into
  11. either separate .data, .rsrc., and .info files that can be downloaded
  12. to a Mac using macput and MacTerminal over a tty line, or into a
  13. single MacBinary format file.  The MacBinary format is generally more
  14. convenient for those with network connections and FTP capability.  The
  15. program is much like the "unpit" program for breaking apart Packit
  16. archive files.
  17.  
  18.             ***** IMPORTANT *****
  19. To extract StuffIt files that have been compressed with the Lempel-Ziv
  20. compression method, unsit pipes the data through the "compress"
  21. program with the appropriate switches, rather than incorporate the
  22. uncompression routines within "unsit".  Therefore, it is necessary to
  23. have the "compress" program on the system and in the search path to
  24. make "unsit" work.  "Compress" is available from the comp.sources.unix
  25. archives.
  26.  
  27. The program syntax is much like unpit and macput/macget, with some added
  28. options:
  29.  
  30.     unsit [-rdulM] [-vqfm] stuffit-file.data
  31.  
  32. Only one of the flags r, d, u, l, or M should be specified.  The
  33. default mode is to create the three macput/MacTerminal compatible
  34. file.  The -M flag will cause the output to be in MacBinary format (a
  35. single file).  This can be swapped (default = MacBinary, -M = macput)
  36. by changing the definitions of DEFAULT_MODE and OTHER_MODE below.  The
  37. -r and -d flags will cause only the resource and data forks to be
  38. written.  The -u flag will cause only the data fork to be written and
  39. to have carriage return characters changed to Unix newline characters.
  40. The -l flag will make the program only list the files in the StuffIt
  41. file.
  42.  
  43. The -v flag causes the program to list the names, sizes, type, and
  44. creators of the files it is writing.  The -q flag causes it to list
  45. the name, type and size of each file and wait for a 'y' or 'n' for
  46. either writing that file or skipping it, respectively.  The -m flag is
  47. used when the input file in in the MacBinary format instead of just
  48. the data fork.  It causes the program to skip the 128 byte MacBinary
  49. header before looking for the StuffIt header.  It is not necessary to
  50. specify the -m flag since the program now checks for MacBinary format
  51. input files and handles them correctly
  52.  
  53. Version 1.5 of the unsit supports extracting files and folders as
  54. implemented by StuffIt 1.5's "Hierarchy Maintained Folder" feature.
  55. Each folder is extracted as a subdirectory on the Unix system with the
  56. files in the folder placed in the corresponding subdirectory.  The -f
  57. option can be used to "flatten" out the hierarchy and unsit will store
  58. all the files in the current directory.  If the query option (-q) is
  59. used and a "n" response is given to a folder name, none of the files
  60. or folders in that folder will be extraced.
  61.  
  62. Some of the program is borrowed from the macput.c/macget.c programs.
  63. Many, many thanks to Raymond Lau, the author of StuffIt, for including
  64. information on the format of the StuffIt archives in the
  65. documentation.  Several changes and enhancements supplied by David
  66. Shanks (cde@atelabs.UUCP) have been incorporated into the program for
  67. doing things like supporting System V and recognizing MacBinary files.
  68. Christopher Bingham <kb@umnstat.stat.umn.edu> supplied some Macbinary
  69. patches.  Code was also borrowed from the macbin program by Jim Budler
  70. for convert macput format files to MacBinary.  I'm always glad to
  71. receive advice, suggestions, or comments about the program so feel free
  72. to send whatever you think would be helpful
  73.  
  74.  
  75.     Author: Allan G. Weber
  76.         weber@sipi.usc.edu
  77.         ...!usc!sipi!weber
  78.     Date:   July 23, 1990
  79.  
  80. */
  81.  
  82. #include <stdio.h>
  83. #include <sys/types.h>
  84. #include <sys/stat.h>
  85. #include <string.h>
  86.  
  87. typedef long OSType;
  88.  
  89. #include "stuffit.h"
  90.  
  91. /*
  92.  * Define the following if your Unix can only handle 14 character file names
  93.  * (e.g. Version 7 and System V).
  94.  */
  95. /* #define SHORTNAMES */
  96.  
  97. /*
  98.  * The following defines the name of the compress program that is used for the
  99.  * uncompression of Lempel-Ziv compressed files.  If the path is set up to
  100.  * include the right directory, this should work.
  101.  */
  102. #define COMPRESS   "compress"
  103.  
  104. #define IOBUFSIZ   4096
  105.  
  106. #define MACBINHDRSIZE  128L
  107.  
  108. #define INIT_CRC 0L
  109. extern unsigned short updcrc();
  110.  
  111. #define INFOBYTES 128
  112.  
  113. #define BYTEMASK 0xff
  114.  
  115. #define S_SIGNATURE    0
  116. #define S_NUMFILES     4
  117. #define S_ARCLENGTH    6
  118. #define S_SIGNATURE2  10
  119. #define    S_VERSION     14
  120. #define SITHDRSIZE    22
  121.  
  122. #define F_COMPRMETHOD    0
  123. #define F_COMPDMETHOD    1
  124. #define F_FNAME          2
  125. #define F_FTYPE         66
  126. #define F_CREATOR       70
  127. #define F_FNDRFLAGS     74
  128. #define F_CREATIONDATE  76
  129. #define F_MODDATE       80
  130. #define F_RSRCLENGTH    84
  131. #define F_DATALENGTH    88
  132. #define F_COMPRLENGTH   92
  133. #define F_COMPDLENGTH   96
  134. #define F_RSRCCRC      100
  135. #define F_DATACRC      102
  136. #define F_HDRCRC       110
  137. #define FILEHDRSIZE    112
  138.  
  139. #define F_NAMELEN 63
  140. #ifdef SHORTNAMES        /* short file names */
  141. # define I_NAMELEN 15        /* 14 char file names + '\0' terminator */
  142. #else
  143. # define I_NAMELEN 69        /* 63 + strlen(".info") + 1 */
  144. #endif
  145.  
  146. /* The following are copied out of macput.c/macget.c */
  147. #define I_NAMEOFF 1
  148. /* 65 <-> 80 is the FInfo structure */
  149. #define I_TYPEOFF 65
  150. #define I_AUTHOFF 69
  151. #define I_FLAGOFF 73
  152. #define I_LOCKOFF 81
  153. #define I_DLENOFF 83
  154. #define I_RLENOFF 87
  155. #define I_CTIMOFF 91
  156. #define I_MTIMOFF 95
  157.  
  158. #define INITED_BUG
  159. #define INITED_OFF    I_FLAGOFF    /* offset to byte with Inited flag */
  160. #define INITED_MASK    (~1)        /* mask to '&' with byte to reset it */
  161.  
  162. #define TEXT 0
  163. #define DATA 1
  164. #define RSRC 2
  165. #define MACPUT 3
  166. #define DUMP 4
  167. #define MACBINARY 5
  168.  
  169. /* Swap the following definitions if you want the output to default to
  170.    MacBinary, and the -M switch to create macput file (.data, .rsrc, .info) */
  171. #define DEFAULT_MODE MACPUT
  172. #define OTHER_MODE   MACBINARY
  173.  
  174. /* #define ADDBIN */    /* add .bin to macbinary file names */
  175.  
  176. #define NODECODE 0
  177. #define DECODE   1
  178.  
  179. #define H_ERROR -1
  180. #define H_EOF    0
  181. #define H_WRITE  1
  182. #define H_SKIP   2
  183.  
  184. struct node {
  185.     int flag, byte;
  186.     struct node *one, *zero;
  187. } nodelist[512], *nodeptr, *read_tree();    /* 512 should be big enough */
  188.  
  189. sitHdr sithdr;
  190.  
  191. char f_info[I_NAMELEN];
  192. char f_data[I_NAMELEN];
  193. char f_rsrc[I_NAMELEN];
  194.  
  195. char info[INFOBYTES];
  196. char mname[F_NAMELEN+1];
  197. char uname[F_NAMELEN+1];
  198. char iobuf[IOBUFSIZ];
  199. char zbuf[128];            /* buffer of zeros to pad MacBinary forks */
  200.  
  201. int mode, txtmode, listonly, verbose, query, flatten;
  202. int bit, chkcrc, numfiles, depth;
  203. int debug = 0;
  204. FILE *infp;
  205.  
  206. long get4();
  207. short get2();
  208. unsigned short write_file();
  209.  
  210. main(argc, argv)
  211. int argc;
  212. char *argv[];
  213. {
  214.     int status;
  215.     int c;
  216.     extern int optind;
  217.     extern char *optarg;
  218.     int errflg;
  219.     int macbin;
  220.  
  221.     mode = DEFAULT_MODE;
  222.     errflg = 0;
  223.     macbin = 0;
  224.     flatten = 0;
  225.     numfiles = 0;
  226.     depth = 0;
  227.  
  228.     while ((c = getopt(argc, argv, "DMdflmqruvx")) != EOF)
  229.     switch (c) {
  230.       case 'r':        /* extract resource fork only */
  231.         mode = RSRC;
  232.         break;
  233.       case 'd':        /* extract data fork only */
  234.         mode = DATA;
  235.         break;
  236.       case 'u':        /* extract data fork as Unix text file */
  237.         mode = TEXT;
  238.         break;
  239.       case 'l':        /* list contents of archive */
  240.         listonly++;
  241.         break;
  242.       case 'q':        /* query user on each extraction */
  243.         query++;
  244.         break;
  245.       case 'v':        /* verbose mode */
  246.         verbose++;
  247.         break;
  248.       case 'x':        /* don't decode data, just dump to files*/
  249.         mode = DUMP;
  250.         break;
  251.       case 'm':        /* input file is in Macbinary format */
  252.         macbin = 1;
  253.         break;
  254.       case 'M':        /* output file in OTHER_MODE */
  255.         mode = OTHER_MODE;
  256.         break;
  257.       case 'f':        /* don't create flat directory tree */
  258.         flatten = 1;
  259.         break;
  260.       case 'D':        /* debugging mode */
  261.         debug = 1;
  262.         break;
  263.       case '?':
  264.         errflg++;
  265.         break;
  266.     }
  267.     if (errflg) {
  268.     usage();
  269.     exit(1);
  270.     }
  271.  
  272.     if (optind == argc) {
  273.     usage();
  274.     exit(1);
  275.     }
  276.     else {
  277.     if ((infp = fopen(argv[optind], "r")) == NULL) {
  278.         fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]);
  279.         exit(1);
  280.     }
  281.     }
  282.  
  283.     if (macbin) {
  284.     if (fseek(infp, MACBINHDRSIZE, 0) == -1) {
  285.         fprintf(stderr, "Can't skip over MacBinary header\n");
  286.         exit(1);
  287.     }
  288.     }
  289.  
  290.     if (readsithdr(&sithdr) == 0) {
  291.     fprintf(stderr, "Can't read file header\n");
  292.     exit(1);
  293.     }
  294.     if (debug) {
  295.     printf("archive header (%d bytes):\n", SITHDRSIZE);
  296.     printf("numFiles=%d, arcLength=%ld, version=%d\n",
  297.            sithdr.numFiles, sithdr.arcLength, sithdr.version & 0xff);
  298.     }
  299.     status = extract("", 0);
  300.     exit((status < 0) ? 1 : 0);
  301. }
  302.  
  303. usage()
  304. {
  305.     fprintf(stderr, "Usage: unsit [-rdulM] [-vqfm] filename\n");
  306. }
  307.  
  308. /*
  309.   extract(parent, skip) - Extract all files from the current folder.
  310.   char *parent;           name of parent folder
  311.   int  skip;              1 to skip all files and folders in this one
  312.                           0 to extract them
  313.  
  314.   returns 1 if came an endFolder record
  315.           0 if EOF
  316.      -1 if error (bad fileHdr, bad file, etc.)
  317. */
  318.  
  319. extract(parent, skip)
  320. char *parent;
  321. int skip;
  322. {
  323.     fileHdr filehdr;
  324.     struct stat sbuf;
  325.     int status, rstat, sstat, skipit;
  326.     char name[256];
  327.  
  328.     while (1) {
  329.     rstat = readfilehdr(&filehdr, skip);
  330.     if (rstat == H_ERROR || rstat == H_EOF) {
  331.         status = rstat;
  332.         break;
  333.     }
  334.     if (debug) {
  335.         printf("file header (%d bytes):\n", FILEHDRSIZE);
  336.         printf("compRMethod=%d, compDMethod=%d\n",
  337.            filehdr.compRMethod, filehdr.compDMethod);
  338.         printf("rsrcLength=%ld, dataLength=%ld\n",
  339.            filehdr.rsrcLength, filehdr.dataLength);
  340.         printf("compRLength=%ld, compDLength=%ld\n",
  341.            filehdr.compRLength, filehdr.compDLength);
  342.         printf("rsrcCRC=%d=0x%04x, dataCRC=%d=0x%04x\n",
  343.            filehdr.rsrcCRC, filehdr.rsrcCRC,
  344.            filehdr.dataCRC, filehdr.dataCRC);
  345.     }
  346.  
  347.     skipit = (rstat == H_SKIP) ? 1 : 0;
  348.  
  349.     if (filehdr.compRMethod == endFolder && 
  350.         filehdr.compDMethod == endFolder) {
  351.         status = 1;        /* finished with this folder */
  352.         break;
  353.     }
  354.     else if (filehdr.compRMethod == startFolder && 
  355.          filehdr.compDMethod == startFolder) {
  356.         if (!listonly && rstat == H_WRITE && !flatten) {
  357.         sstat = stat(uname, &sbuf);
  358.         if (sstat == -1) {    /* directory doesn't exist */
  359.             if (mkdir(uname, 0777) == -1) {
  360.             fprintf(stderr,
  361.                 "Can't create subdirectory %s\n", uname);
  362.             return(-1);
  363.             }
  364.         }
  365.         else {        /* something exists with this name */
  366.             if ((sbuf.st_mode & S_IFMT) != S_IFDIR) {
  367.             fprintf(stderr, "Directory name %s already in use\n",
  368.                 uname);
  369.             return(-1);
  370.             }
  371.         }
  372.         if (chdir(uname) == -1) {
  373.             fprintf(stderr, "Can't chdir to %s\n", uname);
  374.             return(-1);
  375.         }
  376.         sprintf(name,"%s:%s", parent, uname);
  377.         }
  378.         depth++;
  379.         status = extract(name, skipit);
  380.         depth--;
  381.         if (status != 1)
  382.         break;        /* problem with folder */
  383.         if (depth == 0)    /* count how many top-level files done */
  384.         numfiles++;
  385.         if (!flatten)
  386.         chdir("..");
  387.     }
  388.     else {
  389.         if ((status = extractfile(&filehdr, skipit)) != 1)
  390.         break;
  391.         if (depth == 0)    /* count how many top-level files done */
  392.         numfiles++;
  393.     }
  394.     if (numfiles == sithdr.numFiles)
  395.         break;
  396.     }
  397.     return(status);
  398. }
  399.  
  400. extractfile(fh, skip)
  401. fileHdr *fh;
  402. int skip;
  403. {
  404.     unsigned short crc;
  405.     FILE *fp, *fp1;
  406.     int n;
  407.  
  408.     f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */
  409.     /* figure out what file names to use and what to do */
  410.     if (!listonly && !skip) {
  411.     switch (mode) {
  412.       case MACPUT:        /* do both rsrc and data forks */
  413.         sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname);
  414.         sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname);
  415.         sprintf(f_info, "%.*s.info", I_NAMELEN - 6, uname);
  416.         break;
  417.       case RSRC:        /* rsrc fork only */
  418.         sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname);
  419.         break;
  420.       case DATA:        /* data fork only */
  421.       case TEXT:
  422.         sprintf(f_data, "%.*s", I_NAMELEN - 1, uname);
  423.         break;
  424.       case DUMP:        /* for debugging, dump data as is */
  425.         sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname);
  426.         sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname);
  427.         fh->compRMethod = fh->compDMethod = noComp;
  428.         break;
  429.       case MACBINARY:    /* output file in MacBinary format */
  430.         sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname);
  431.         sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname);
  432. #ifndef ADDBIN
  433.         sprintf(f_info, "%.*s", I_NAMELEN - 1, uname);
  434. #else
  435.         sprintf(f_info, "%.*s.bin", I_NAMELEN - 5, uname);
  436. #endif /*ADDBIN*/
  437.         break;
  438.     }
  439.     }
  440.  
  441.     fp = NULL;            /* so we can tell if a file is open */
  442.     if (f_info[0] != '\0' && check_access(f_info) != -1) {
  443.     fp = fopen(f_info, "w");
  444.     if (fp == NULL) {
  445.         perror(f_info);
  446.         exit(1);
  447.     }
  448.     if (mode == MACBINARY) { /* convert to MacBinary header */
  449.         /* taken from the macbin program */
  450.         if (info[74] & 0x40) info[81] = '\1'; /* protected */
  451.         info[74] = '\0'; /* clear zero2 */
  452.         info[82] = '\0'; /* force zero3 clear */
  453.     }
  454.     fwrite(info, 1, INFOBYTES, fp);
  455.     }
  456.  
  457.     if (f_rsrc[0] != '\0') {
  458.     txtmode = 0;
  459.     crc = write_file(f_rsrc, fh->compRLength,
  460.              fh->rsrcLength, fh->compRMethod);
  461.     if (chkcrc && fh->rsrcCRC != crc) {
  462.         fprintf(stderr,
  463.             "CRC error on resource fork: need 0x%04x, got 0x%04x\n",
  464.             fh->rsrcCRC, crc);
  465.         return(-1);
  466.     }
  467.     }
  468.     else {
  469.     fseek(infp, (long) fh->compRLength, 1);
  470.     }
  471.     if (f_data[0] != '\0') {
  472.     txtmode = (mode == TEXT);
  473.     crc = write_file(f_data, fh->compDLength,
  474.              fh->dataLength, fh->compDMethod);
  475.     if (chkcrc && fh->dataCRC != crc) {
  476.         fprintf(stderr,
  477.             "CRC error on data fork: need 0x%04x, got 0x%04x\n",
  478.             fh->dataCRC, crc);
  479.         return(-1);
  480.     }
  481.     }
  482.     else {
  483.     fseek(infp, (long) fh->compDLength, 1);
  484.     }
  485.     if (fp != NULL) {
  486.     /* if Macbinary output, copy the data and resource forks to the
  487.        end of the info file, and pad each to multiples of 128 bytes. */
  488.     if (mode == MACBINARY) {
  489.         fp1 = fopen(f_data, "r"); /* re-open the file we just wrote */
  490.         if (fp1 == NULL) {
  491.         perror(f_data);
  492.         exit(1);
  493.         }
  494.         while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0)
  495.         fwrite(iobuf, 1, n, fp); /* append it to the info file */
  496.         /* pad out to multiple of 128 if in MacBinary format */
  497.         n = fh->dataLength % 128;
  498.         if (n > 0)
  499.         outc(zbuf, 128 - n, fp);
  500.         fclose(fp1);
  501.         unlink(f_data);
  502.         fp1 = fopen(f_rsrc, "r"); /* re-open the file we just wrote */
  503.         if (fp1 == NULL) {
  504.         perror(f_rsrc);
  505.         exit(1);
  506.         }
  507.         while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0)
  508.         fwrite(iobuf, 1, n, fp); /* append it to the info file */
  509.         /* pad out to multiple of 128 if in MacBinary format */
  510.         n = fh->rsrcLength % 128;
  511.         if (n > 0)
  512.         outc(zbuf, 128 - n, fp);
  513.         fclose(fp1);
  514.         unlink(f_rsrc);
  515.     }
  516.     fclose(fp);
  517.     }
  518.     return(1);
  519. }
  520.  
  521. readsithdr(s)
  522. sitHdr *s;
  523. {
  524.     char temp[FILEHDRSIZE];
  525.     int count = 0;
  526.  
  527.     for (;;) {
  528.     if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) {
  529.         fprintf(stderr, "Can't read file header\n");
  530.         return(0);
  531.     }
  532.         
  533.     if (strncmp(temp + S_SIGNATURE,  "SIT!", 4) == 0 &&
  534.         strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) {
  535.         s->numFiles = get2(temp + S_NUMFILES);
  536.         s->arcLength = get4(temp + S_ARCLENGTH);
  537.         return(1);
  538.     }
  539.     
  540.     if (++count == 2) {
  541.         fprintf(stderr, "Not a StuffIt file\n");
  542.         return(0);
  543.     }
  544.     
  545.     if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) !=
  546.         FILEHDRSIZE - SITHDRSIZE) {
  547.         fprintf(stderr, "Can't read file header\n");
  548.         return(0);
  549.     }
  550.     
  551.     if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 &&
  552.         strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) {    /* MacBinary format */
  553.         fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1);    /* Skip over header */
  554.     }
  555.     }
  556. }
  557.  
  558. /*
  559.   readfilehdr - reads the file header for each file and the folder start
  560.   and end records.
  561.  
  562.   returns: H_ERROR = error
  563.        H_EOF   = EOF
  564.        H_WRITE = write file/folder
  565.        H_SKIP  = skip file/folder
  566. */
  567.  
  568. readfilehdr(f, skip)
  569. fileHdr *f;
  570. int skip;
  571. {
  572.     unsigned short crc;
  573.     int i, n, write_it, isfolder;
  574.     char hdr[FILEHDRSIZE];
  575.     char ch, *mp, *up;
  576.     char *tp, temp[10];
  577.  
  578.     for (i = 0; i < INFOBYTES; i++)
  579.     info[i] = '\0';
  580.  
  581.     /* read in the next file header, which could be folder start/end record */
  582.     n = fread(hdr, 1, FILEHDRSIZE, infp);
  583.     if (n == 0)            /* return 0 on EOF */
  584.     return(H_EOF);
  585.     else if (n != FILEHDRSIZE) {
  586.     fprintf(stderr, "Can't read file header\n");
  587.     return(H_ERROR);
  588.     }
  589.  
  590.     /* check the CRC for the file header */
  591.     crc = INIT_CRC;
  592.     crc = updcrc(crc, hdr, FILEHDRSIZE - 2);
  593.     f->hdrCRC = get2(hdr + F_HDRCRC);
  594.     if (f->hdrCRC != crc) {
  595.     fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n",
  596.         f->hdrCRC, crc);
  597.     return(H_ERROR);
  598.     }
  599.  
  600.     /* grab the name of the file or folder */
  601.     n = hdr[F_FNAME] & BYTEMASK;
  602.     if (n > F_NAMELEN)
  603.     n = F_NAMELEN;
  604.     info[I_NAMEOFF] = n;
  605.     copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n);
  606.     strncpy(mname, hdr + F_FNAME + 1, n);
  607.     mname[n] = '\0';
  608.     /* copy to a string with no illegal Unix characters in the file name */
  609.     mp = mname;
  610.     up = uname;
  611.     while ((ch = *mp++) != '\0') {
  612.     if (ch <= ' ' || ch > '~' || strchr("/!()[]*<>?\\\"$\';&`", ch) != NULL)
  613.         ch = '_';
  614.     *up++ = ch;
  615.     }
  616.     *up = '\0';
  617.  
  618.     /* get lots of other stuff from the header */
  619.     f->compRMethod = hdr[F_COMPRMETHOD];
  620.     f->compDMethod = hdr[F_COMPDMETHOD];
  621.     f->rsrcLength = get4(hdr + F_RSRCLENGTH);
  622.     f->dataLength = get4(hdr + F_DATALENGTH);
  623.     f->compRLength = get4(hdr + F_COMPRLENGTH);
  624.     f->compDLength = get4(hdr + F_COMPDLENGTH);
  625.     f->rsrcCRC = get2(hdr + F_RSRCCRC);
  626.     f->dataCRC = get2(hdr + F_DATACRC);
  627.  
  628.     /* if it's an end folder record, don't need to do any more */
  629.     if (f->compRMethod == endFolder && f->compDMethod == endFolder)
  630.     return(H_WRITE);
  631.  
  632.     /* prepare an info file in case its needed */
  633.  
  634.     copy(info + I_TYPEOFF, hdr + F_FTYPE, 4);
  635.     copy(info + I_AUTHOFF, hdr + F_CREATOR, 4);
  636.     copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2);
  637. #ifdef INITED_BUG
  638.     info[INITED_OFF] &= INITED_MASK; /* reset init bit */
  639. #endif
  640.     copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4);
  641.     copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4);
  642.     copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4);
  643.     copy(info + I_MTIMOFF, hdr + F_MODDATE, 4);
  644.  
  645.     isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder;
  646.     
  647.     /* list the file name if verbose or listonly mode, also if query mode */
  648.     if (skip)            /* skip = 1 if skipping all in this folder */
  649.     write_it = 0;
  650.     else {
  651.     write_it = 1;
  652.     if (listonly || verbose || query) {
  653.         for (i = 0; i < depth; i++)
  654.         putchar(' ');
  655.         if (isfolder)
  656.         printf("Folder: \"%s\"", uname);
  657.         else
  658.         printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld",
  659.                uname, hdr + F_FTYPE, hdr + F_CREATOR,
  660.                f->dataLength, f->rsrcLength);
  661.         if (query) {    /* if querying, check with the boss */
  662.         printf(" ? ");
  663.         fgets(temp, sizeof(temp) - 1, stdin);
  664.         tp = temp;
  665.         write_it = 0;
  666.         while (*tp != '\0') {
  667.             if (*tp == 'y' || *tp == 'Y') {
  668.             write_it = 1;
  669.             break;
  670.             }
  671.             else
  672.             tp++;
  673.         }
  674.         }
  675.         else        /* otherwise, terminate the line */
  676.         putchar('\n');
  677.     }
  678.     }
  679.     return(write_it ? H_WRITE : H_SKIP);
  680. }
  681.  
  682. check_access(fname)    /* return 0 if OK to write on file fname, -1 otherwise */
  683. char *fname;
  684. {
  685.     char temp[10], *tp;
  686.  
  687.     if (access(fname, 0) == -1) {
  688.     return(0);
  689.     }
  690.     else {
  691.     printf("%s exists.  Overwrite? ", fname);
  692.     fgets(temp, sizeof(temp) - 1, stdin);
  693.     tp = temp;
  694.     while (*tp != '\0') {
  695.         if (*tp == 'y' || *tp == 'Y') {
  696.         return(0);
  697.         }
  698.         else
  699.         tp++;
  700.     }
  701.     }
  702.     return(-1);
  703. }
  704.  
  705. unsigned short write_file(fname, ibytes, obytes, type)
  706. char *fname;
  707. unsigned long ibytes, obytes;
  708. unsigned char type;
  709. {
  710.     unsigned short crc;
  711.     int i, n, ch, lastch;
  712.     FILE *outf;
  713.     char temp[256];
  714.  
  715.     crc = INIT_CRC;
  716.     chkcrc = 1;            /* usually can check the CRC */
  717.  
  718.     if (check_access(fname) == -1) {
  719.     fseek(infp, ibytes, 1);
  720.     chkcrc = 0;        /* inhibit crc check if file not written */
  721.         return(-1);
  722.     }
  723.     
  724.     switch (type) {
  725.       case noComp:        /* no compression */
  726.     if (verbose)
  727.         printf("compression=none\n");
  728.     outf = fopen(fname, "w");
  729.     if (outf == NULL) {
  730.         perror(fname);
  731.         exit(1);
  732.     }
  733.     while (ibytes > 0) {
  734.         n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  735.         n = fread(iobuf, 1, n, infp);
  736.         if (n == 0)
  737.         break;
  738.         crc = updcrc(crc, iobuf, n);
  739.         outc(iobuf, n, outf);
  740.         ibytes -= n;
  741.     }
  742.     fclose(outf);
  743.     break;
  744.       case rleComp:        /* run length encoding */
  745.     if (verbose)
  746.         printf("compression=rle\n");
  747.     outf = fopen(fname, "w");
  748.     if (outf == NULL) {
  749.         perror(fname);
  750.         exit(1);
  751.     }
  752.     while (ibytes > 0) {
  753.         ch = getc(infp) & 0xff;
  754.         ibytes--;
  755.         if (ch == 0x90) {    /* see if its the repeat marker */
  756.         n = getc(infp) & 0xff; /* get the repeat count */
  757.         ibytes--;
  758.         if (n == 0) { /* 0x90 was really an 0x90 */
  759.             iobuf[0] = 0x90;
  760.             crc = updcrc(crc, iobuf, 1);
  761.             outc(iobuf, 1, outf);
  762.         }
  763.         else {
  764.             n--;
  765.             for (i = 0; i < n; i++)
  766.             iobuf[i] = lastch;
  767.             crc = updcrc(crc, iobuf, n);
  768.             outc(iobuf, n, outf);
  769.         }
  770.         }
  771.         else {
  772.         iobuf[0] = ch;
  773.         crc = updcrc(crc, iobuf, 1);
  774.         lastch = ch;
  775.         outc(iobuf, 1, outf);
  776.         }
  777.     }
  778.     fclose(outf);
  779.     break;
  780.       case lzwComp:        /* LZW compression */
  781.     if (verbose)
  782.         printf("compression=lzw\n");
  783.     sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 ");
  784.     if (txtmode) {
  785.         strcat(temp, "| tr \'\\015\' \'\\012\' ");
  786.         chkcrc = 0;        /* can't check CRC in this case */
  787.     }
  788.     strcat(temp, "> '");
  789.     strcat(temp, fname);
  790.     strcat(temp, "'");
  791.     outf = popen(temp, "w");
  792.     if (outf == NULL) {
  793.         perror(fname);
  794.         exit(1);
  795.     }
  796.     while (ibytes > 0) {
  797.         n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  798.         n = fread(iobuf, 1, n, infp);
  799.         if (n == 0)
  800.         break;
  801.         fwrite(iobuf, 1, n, outf);
  802.         ibytes -= n;
  803.     }
  804.     pclose(outf);
  805.     if (chkcrc) {
  806.         outf = fopen(fname, "r"); /* read the file to get CRC value */
  807.         if (outf == NULL) {
  808.         perror(fname);
  809.         exit(1);
  810.         }
  811.         while (1) {
  812.         n = fread(iobuf, 1, IOBUFSIZ, outf);
  813.         if (n == 0)
  814.             break;
  815.         crc = updcrc(crc, iobuf, n);
  816.         }
  817.         fclose(outf);
  818.     }
  819.     break;
  820.       case hufComp:        /* Huffman compression */
  821.     if (verbose)
  822.         printf("compression=Huffman\n");
  823.     outf = fopen(fname, "w");
  824.     if (outf == NULL) {
  825.         perror(fname);
  826.         exit(1);
  827.     }
  828.     nodeptr = nodelist;
  829.     bit = 0;        /* put us on a byte boundary */
  830.     read_tree();
  831.     while (obytes > 0) {
  832.         n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes;
  833.         for (i = 0; i < n; i++)
  834.         iobuf[i] = gethuffbyte(DECODE);
  835.         crc = updcrc(crc, iobuf, n);
  836.         outc(iobuf, n, outf);
  837.         obytes -= n;
  838.     }
  839.     fclose(outf);
  840.     break;
  841.       default:
  842.     fprintf(stderr, "Unknown compression method\n");
  843.     chkcrc = 0;        /* inhibit crc check if file not written */
  844.     return(-1);
  845.     }
  846.  
  847.     return(crc & 0xffff);
  848. }
  849.  
  850. outc(p, n, fp)
  851. char *p;
  852. int n;
  853. FILE *fp;
  854. {
  855.     register char *p1;
  856.     register int i;
  857.     if (txtmode) {
  858.     for (i = 0, p1 = p; i < n; i++, p1++)
  859.         if ((*p1 & BYTEMASK) == '\r')
  860.         *p1 = '\n';
  861.     }
  862.     fwrite(p, 1, n, fp);
  863. }
  864.  
  865. long get4(bp)
  866. char *bp;
  867. {
  868.     register int i;
  869.     long value = 0;
  870.  
  871.     for (i = 0; i < 4; i++) {
  872.     value <<= 8;
  873.     value |= (*bp & BYTEMASK);
  874.     bp++;
  875.     }
  876.     return(value);
  877. }
  878.  
  879. short get2(bp)
  880. char *bp;
  881. {
  882.     register int i;
  883.     int value = 0;
  884.  
  885.     for (i = 0; i < 2; i++) {
  886.     value <<= 8;
  887.     value |= (*bp & BYTEMASK);
  888.     bp++;
  889.     }
  890.     return(value);
  891. }
  892.  
  893. copy(p1, p2, n)
  894. char *p1, *p2;
  895. int n;
  896. {
  897.     while (n-- > 0)
  898.         *p1++ = *p2++;
  899. }
  900.  
  901. /* This routine recursively reads the Huffman encoding table and builds
  902.    and decoding tree. */
  903.  
  904. struct node *read_tree()
  905. {
  906.     struct node *np;
  907.     np = nodeptr++;
  908.     if (getbit() == 1) {
  909.         np->flag = 1;
  910.         np->byte = gethuffbyte(NODECODE);
  911.     }
  912.     else {
  913.         np->flag = 0;
  914.         np->zero = read_tree();
  915.         np->one  = read_tree();
  916.     }
  917.     return(np);
  918. }
  919.  
  920. /* This routine returns the next bit in the input stream (MSB first) */
  921.  
  922. getbit()
  923. {
  924.     static char b;
  925.     if (bit == 0) {
  926.         b = getc(infp) & 0xff;
  927.         bit = 8;
  928.     }
  929.     bit--;
  930.     return((b >> bit) & 1);
  931. }
  932.  
  933. /* This routine returns the next 8 bits.  If decoding is on, it finds the
  934. byte in the decoding tree based on the bits from the input stream.  If
  935. decoding is not on, it either gets it directly from the input stream or
  936. puts it together from 8 calls to getbit(), depending on whether or not we
  937. are currently on a byte boundary
  938. */
  939. gethuffbyte(decode)
  940. int decode;
  941. {
  942.     register struct node *np;
  943.     register int i, b;
  944.     if (decode == DECODE) {
  945.         np = nodelist;
  946.         while (np->flag == 0)
  947.             np = (getbit()) ? np->one : np->zero;
  948.         b = np->byte;
  949.     }
  950.     else {
  951.         if (bit == 0)    /* on byte boundary? */
  952.             b = getc(infp) & 0xff;
  953.         else {        /* no, put a byte together */
  954.             b = 0;
  955.             for (i = 8; i > 0; i--) {
  956.                 b = (b << 1) + getbit();
  957.             }
  958.         }
  959.     }
  960.     return(b);
  961. }
  962.